home *** CD-ROM | disk | FTP | other *** search
/ Tech Arsenal 1 / Tech Arsenal (Arsenal Computer).ISO / tek-04 / ddj0492.zip / HANDPRIN.URC / Recognizer / Analyzer.c next >
Text File  |  1992-03-11  |  12KB  |  458 lines

  1. /* All rights reserved. Copyright Ron Avitzur - 1992
  2. */
  3.  
  4. #include "Recognizer.h"
  5.  
  6. #define ANGLE_THRESHOLD    5
  7. #define DOT_THRESHHOLD    (Wacom?40:4)
  8. #define PRE_ABS             (Wacom?40:4)
  9. #define PRE_RATIO_X         8
  10. #define PRE_RATIO_Y         8
  11. #define FIRST_RATIO_X     4
  12. #define FIRST_RATIO_Y     4
  13. #define MULTI_TIME_OUT TRAINING_TIME_OUT
  14.  
  15. short CollectStroke(where,theStroke)
  16.  Point    where;
  17.  StrokePtr theStroke;
  18.      {
  19.      long down_time;
  20.      long Xmin,Xmax,Ymin,Ymax;
  21.     short still_down = TRUE,i = 0, result = 0;
  22.     Point last,drawme;
  23.     PenState thePnState;
  24.      GetPenState ( &thePnState );
  25.  
  26.     if (Wacom) {
  27.         drawme.h = TAB_TO_SCREEN_X(where.h);
  28.         drawme.v = TAB_TO_SCREEN_Y(where.v);
  29.         GlobalToLocal(&drawme);
  30.         }
  31.     else
  32.         drawme = where;
  33.     PenMode(patXor);
  34.     PenSize(1,1);
  35.     MoveTo(drawme.h,drawme.v);
  36.     LineTo(drawme.h,drawme.v);
  37.  
  38.     last = where;
  39.     Xmin = Xmax = where.h;
  40.     Ymin = Ymax = where.v;
  41.     theStroke->Ink[i++] = where;
  42.  
  43.     down_time = TickCount();
  44.     while (still_down && i < MAX_POINTS) {
  45.  
  46.         if (PauseIsMouse) {
  47.             if (!Training && TickCount() - down_time > MULTI_TIME_OUT &&
  48.                 Xmax - Xmin < DOT_THRESHHOLD &&
  49.                 Ymax - Ymin < DOT_THRESHHOLD)
  50.             {
  51.             short j;
  52.             for (j = 0; j < i; j++) {
  53.                 where = theStroke->Ink[j];
  54.                 if (Wacom) {
  55.                     drawme.h = TAB_TO_SCREEN_X(where.h);
  56.                     drawme.v = TAB_TO_SCREEN_Y(where.v);
  57.                     GlobalToLocal(&drawme);
  58.                     }
  59.                 else
  60.                     drawme = where;
  61.  
  62.                 MoveTo(drawme.h,drawme.v);
  63.                 LineTo(drawme.h,drawme.v);
  64.                 }
  65.             result = 1;
  66.             goto DONE;
  67.             }
  68.         }
  69.  
  70.         if (Wacom) {
  71.             if (TRecord->updateFlags & FRESHDATA)
  72.                     TRecord->updateFlags &= ~FRESHDATA;
  73.             else    continue;
  74.             where.h = TRecord->xCoord;
  75.             where.v = TRecord->yCoord;
  76.             drawme.h = TAB_TO_SCREEN_X(TRecord->xCoord);
  77.             drawme.v = TAB_TO_SCREEN_Y(TRecord->yCoord);
  78.             GlobalToLocal(&drawme);
  79.             still_down = TRecord->buttons;
  80.             }
  81.         else {
  82.             GetMouse(&where);
  83.             drawme = where;
  84.             still_down = (StillDown() || StillDown()|| StillDown() || StillDown());
  85.             }
  86.         if (where.h == last.h && where.v == last.v) continue;
  87.         if (where.h > Xmax) Xmax = where.h;
  88.         if (where.h < Xmin) Xmin = where.h;
  89.         if (where.v > Ymax) Ymax = where.v;
  90.         if (where.v < Ymin) Ymin = where.v;
  91.         theStroke->Ink[i++] = last = where;
  92.         MoveTo(drawme.h,drawme.v); LineTo(drawme.h,drawme.v);
  93.         }
  94.     PenNormal();
  95.     theStroke->Ink_Num = i;
  96.     theStroke->end_time        = TickCount();
  97.     theStroke->Height            = Ymax - Ymin + 1;
  98.     theStroke->Width            = Xmax - Xmin + 1;
  99.     theStroke->Xmin            = Xmin;
  100.     theStroke->Xmax            = Xmax;
  101.     theStroke->Ymin            = Ymin;
  102.     theStroke->Ymax            = Ymax;
  103.     theStroke->Aspect_Ratio    = ((double)theStroke->Height - theStroke->Width) /
  104.                                                     (theStroke->Height + theStroke->Width);
  105.     theStroke->Aspect_Ratio    = (double)theStroke->Height / theStroke->Width;
  106.     Simplify(theStroke,theStroke->Ink,theStroke->Ink_Num);
  107.     Analyze(theStroke);
  108.     result = 0;
  109.  
  110. DONE:
  111.     SetPenState ( &thePnState );
  112.     return result;
  113.     }
  114.  
  115. short Process3(register Point *P,register Point *Q,short num,short,short,short,short);
  116. short Process3(register Point *P,register Point *Q,short num,short xd,short yd,short xd2,short yd2)
  117.     {
  118.     register short i,n;
  119.     register short dx,dy;
  120.     
  121.     n = 0;
  122.     P[0] = Q[0];
  123.     for (i = 1; i < num - 1; i++) {
  124.         dx = Q[i].h - P[n].h; dx = ABS(dx);
  125.         dy = Q[i].v - P[n].v; dy = ABS(dy);
  126.         if (dx + dy < PRE_ABS)    continue;
  127.         if (dx < xd && dy < yd)    continue;
  128.         if (n == 0 && dx < xd2 && dy < yd2) continue;
  129.         n++;
  130.         P[n] = Q[i];
  131.         }
  132.     dx = Q[num - 1].h - P[n].h;
  133.     dy = Q[num - 1].v - P[n].v;
  134.     if (ABS(dx) + ABS(dy) > PRE_ABS)
  135.         n++;
  136.     P[n] = Q[num - 1];
  137.     return n + 1;
  138.     }
  139.  
  140. short Dt(short t1,short t2);
  141. short Dt(short t1,short t2)
  142.     {
  143.     short dt,a;
  144.     dt = t2 - t1;
  145.     a = ABS(dt);
  146.     if            (a == 180) dt = 180;
  147.     else if    (a >  180) {
  148.         if            (t2>0) dt -= 360;
  149.         else if     (t1>0) dt += 360;
  150.         }
  151.     return dt;
  152.     }
  153.  
  154. short Process4(Point *P,Point *Q,short N);
  155. short Process4(Point *P,Point *Q,short N)
  156.     {
  157.     Point A,B,C;
  158.     short dt,t1,i,n = 1;
  159.     
  160.     A = P[0] = Q[0];
  161.     B = P[1] = Q[1];
  162.     t1 = ATAN2(B.v - A.v,B.h - A.h);
  163.     for (i = 2; i < N; i++) {
  164.         C = Q[i];
  165.         dt = Dt(t1,ATAN2(C.v - B.v,C.h - B.h));
  166.         if (ABS(dt) < ANGLE_THRESHOLD)
  167.             P[n] = B = C;
  168.         else {
  169.             A = B;
  170.             B = C;
  171.             t1 = ATAN2(B.v - A.v,B.h - A.h);
  172.             n++;
  173.             P[n] = C;
  174.             }
  175.         }
  176.     return n + 1;
  177.     }
  178.  
  179. void    Simplify(StrokePtr theStroke,Point *Ink,short N)
  180.     {
  181.     Point Q[MAX_POINTS];
  182.     short w = theStroke->Width, h = theStroke->Height;
  183.     short xd  = w / PRE_RATIO_X,
  184.             yd  = h / PRE_RATIO_Y,
  185.             xd2 = w / FIRST_RATIO_X,
  186.             yd2 = h / FIRST_RATIO_Y;
  187.     double ar = theStroke->Aspect_Ratio;
  188.     if          (ar < 0.2) {
  189.             theStroke->P[0] = theStroke->Ink[0];
  190.             theStroke->P[1] = theStroke->Ink[theStroke->Ink_Num/2];
  191.             theStroke->P[2] = theStroke->Ink[theStroke->Ink_Num - 1];
  192.             N = 3;
  193.             }
  194.     else if (ar > 5.0)
  195.             N = Process3(theStroke->P,Ink,N,2*w,yd,2*w,yd2);
  196.     else    N = Process3(theStroke->P,Ink,N,xd,yd,xd2,yd2);
  197.     theStroke->N = N;
  198.     }
  199.  
  200. #define DirectionA(t) ('0' + ((t + 10 + 45 + 180) / 90) % 4)
  201. #define DirectionB(t) ('0' + ((t + 10 + 00 + 180) / 90) % 4)
  202. #define Directiona(t) ('0' + ((t + 10 + 22 + 180) / 90) % 4)
  203. #define Directionb(t) ('0' + ((t + 10 - 22 + 180) / 90) % 4)
  204. #define DirectionC(t) ('0' + ((t + 10 + 22 + 180) / 45) % 8)
  205. #define DirectionD(p) ('0' + (4*((p).h - theStroke->XminT) / theStroke->WidthT))
  206. #define DirectionE(p) ('0' + (4*((p).v - theStroke->YminT) / theStroke->HeightT))
  207. #define DirectionU(t) ('0' + ((t + 00 + 45 + 180) / 90) % 4)
  208.  
  209. #define FOO(name,fxn,type,array,end)        \
  210. void name(char *s,StrokePtr theStroke);    \
  211. void name(s,theStroke)                            \
  212.  register char *s;                                \
  213.  StrokePtr    theStroke;                            \
  214.     {                                                    \
  215.     register short i,d,n = 0;                    \
  216.     register type *T = theStroke->array;    \
  217.     s[0] = fxn(*T++);                                \
  218.     i = theStroke->N - end;                        \
  219.     while (i-- > 0) {                                \
  220.         d = fxn(*T++);                                \
  221.         if (s[n] != d)                                \
  222.             s[++n] = d;                                \
  223.         }                                                \
  224.     s[++n] = 0;                                        \
  225.     }
  226.  
  227. FOO(Fxn1,DirectionA,short,T,2)
  228. FOO(Fxn2,DirectionB,short,T,2)
  229. FOO(Fxna,Directiona,short,T,2)
  230. FOO(Fxnb,Directionb,short,T,2)
  231. FOO(Fxn3,DirectionC,short,T,2)
  232. FOO(FxnU,DirectionU,short,U,3)
  233. FOO(Fxn4,DirectionD,Point,P,1)
  234. FOO(Fxn5,DirectionE,Point,P,1)
  235.  
  236. #define Directionc(p) '0' + ((3*((p).h - theStroke->XminT) / theStroke->WidthT) + \
  237.     3*(3*((p).v - theStroke->YminT) / theStroke->HeightT))
  238.  
  239. void Fxnc(char *s,StrokePtr theStroke);
  240. void Fxnc(register char *s,StrokePtr theStroke)
  241.     {
  242.     register short i= theStroke->N - 1,d,n = -1;
  243.     register Point p,*T = theStroke->P;
  244.     while (i-- > 0) {
  245.         p = *T++;
  246.         d = Directionc(p);
  247.         if (d == '4') continue;
  248.         if (d > '4') d -= 1;
  249.         if (n == -1 || s[n] != d)
  250.             s[++n] = d;
  251.         }
  252.     s[++n] = 0;
  253.     }
  254.  
  255. #define Directiond(p) '0' + ((2*((p).h - theStroke->XminT) / theStroke->WidthT) + \
  256.     2*(2*((p).v - theStroke->YminT) / theStroke->HeightT))
  257. #define IN_MIDDLE(p) (Directionc(p) == '4')
  258. void Fxnd(char *s,StrokePtr theStroke);
  259. void Fxnd(register char *s,StrokePtr theStroke)
  260.     {
  261.     register short i= theStroke->N - 1,d,n = -1;
  262.     register Point p,*T = theStroke->P;
  263.     while (i-- > 0) {
  264.         p = *T++;
  265.         d = Directiond(p);
  266.         if (IN_MIDDLE(p)) continue;
  267.         if (n == -1 || s[n] != d)
  268.             s[++n] = d;
  269.         }
  270.     s[++n] = 0;
  271.     }
  272.  
  273. void ComputeT(theStroke)
  274.  StrokePtr theStroke;
  275.     {
  276.     register short *T  = theStroke->T;
  277.     register short *U  = theStroke->U;
  278.     register Point *P  = theStroke->P;
  279.     register short i,N = theStroke->N;
  280.     for (i = 0; i < N - 1; i++)
  281.         T[i] = ATAN2(P[i+1].v-P[i].v,P[i+1].h-P[i].h);
  282.     T[N-1] = T[N-2];
  283.     for (i = 0; i < N - 2; i++)
  284.         U[i] = Dt(T[i],T[i+1]);
  285.     }
  286.  
  287. void Analyze(theStroke)
  288.  register StrokePtr theStroke;
  289.     {
  290.     char s[100];
  291.     register short *T = theStroke->T;
  292.     register Point *P = theStroke->P;
  293.     register short  N = theStroke->N;
  294.     short Height = theStroke->Height,
  295.             Width  = theStroke->Width,i;
  296.  
  297.     if (Strokes)
  298.         {
  299.          StrokePtr    lastStroke = Strokes->items[Strokes->num_items - 1];
  300.          Point lastPoint = lastStroke->P[lastStroke->N - 1];
  301.          
  302.         theStroke->XmaxT = MAX(theStroke->Xmax,lastStroke->XmaxT);
  303.         theStroke->XminT = MIN(theStroke->Xmin,lastStroke->XminT);
  304.         theStroke->YmaxT = MAX(theStroke->Ymax,lastStroke->YmaxT);
  305.         theStroke->YminT = MIN(theStroke->Ymin,lastStroke->YminT);
  306.         theStroke->HeightT = (theStroke->YmaxT - theStroke->YminT + 1);
  307.         theStroke->WidthT = (theStroke->XmaxT - theStroke->XminT + 1);
  308.         }
  309.     else {
  310.         theStroke->XmaxT = theStroke->Xmax;
  311.         theStroke->XminT = theStroke->Xmin;
  312.         theStroke->YmaxT = theStroke->Ymax;
  313.         theStroke->YminT = theStroke->Ymin;
  314.         theStroke->HeightT = theStroke->Height;
  315.         theStroke->WidthT = theStroke->Width;
  316.         }
  317.  
  318.     ComputeT(theStroke);
  319.     for (i=0;i<NUM_FEATURES;i++) {
  320.         (*HashFunctions[i])(s,theStroke);
  321.         ConvertStringToLong(s,&theStroke->S[i],Bits[i]);
  322.         }
  323.     theStroke->IsDot = ((Height < DOT_THRESHHOLD && Width < DOT_THRESHHOLD) || N == 1);
  324.     }
  325.  
  326. void ComputeBBox()
  327.     {
  328.     long Xmax,Xmin,Ymax,Ymin;
  329.     short            i;
  330.      StrokePtr    theStroke;
  331.      
  332.     theStroke = Strokes->items[0];
  333.     Xmax = theStroke->Xmax; Xmin = theStroke->Xmin;
  334.     Ymax = theStroke->Ymax; Ymin = theStroke->Ymin;
  335.  
  336.     for (i = 1; i < Strokes->num_items; i++) {
  337.         theStroke = Strokes->items[i];
  338.         if (theStroke->Xmax > Xmax) Xmax = theStroke->Xmax;
  339.         if (theStroke->Ymax > Ymax) Ymax = theStroke->Ymax;
  340.         if (theStroke->Xmin < Xmin) Xmin = theStroke->Xmin;
  341.         if (theStroke->Ymin < Ymin) Ymin = theStroke->Ymin;
  342.         }
  343.  
  344.     if (Wacom) {
  345.         BBox.top        = TAB_TO_SCREEN_Y(Ymin);
  346.         BBox.bottom    = TAB_TO_SCREEN_Y(Ymax);
  347.         BBox.left    = TAB_TO_SCREEN_X(Xmin);
  348.         BBox.right    = TAB_TO_SCREEN_X(Xmax);
  349.         GlobalToLocal(&topLeft(BBox));
  350.         GlobalToLocal(&botRight(BBox));
  351.         }
  352.     else {
  353.         BBox.top        = Ymin;
  354.         BBox.bottom    = Ymax;
  355.         BBox.left    = Xmin;
  356.         BBox.right    = Xmax;
  357.         }
  358.     }
  359.  
  360. short StrokeMayContinue(void);
  361. short StrokeMayContinue()
  362.     {
  363.     GesturePattern *gp;
  364.     short i,index;
  365.     StrokePtr lastStroke = Strokes->items[Strokes->num_items - 1];
  366.     List matches = lastStroke->matches;
  367.     
  368.     if (matches == NIL) return FALSE;
  369.     if (matches->num_items == 0) return FALSE;
  370.     
  371.     for (i = 0; i < matches->num_items; i++) {
  372.         index = (long)matches->items[i] & 0xFFFF;
  373.         gp = patterns->items[index];
  374.         if (gp->strokes->num_items > Strokes->num_items)
  375.             return TRUE;
  376.         }
  377.     return FALSE;
  378.     }
  379.  
  380. short TryCollectGesture(where,when)
  381.  Point where;
  382.  long    when;
  383.     {
  384.     short            i,j,code;
  385.      StrokePtr    theStroke;
  386.  
  387.     FreeStrokes();
  388.      theStroke = calloc(1,sizeof(StrokeData));
  389.     if (theStroke == NIL) {SysBeep(1); Debugger(); ExitToShell();}
  390.      theStroke->start_time = when;
  391.     if (CollectStroke(where,theStroke))
  392.         {
  393.         if (PauseIsMouse) {
  394.             free(theStroke);
  395.             return -1;
  396.             }
  397.         }
  398.  
  399.     FlushEvents(everyEvent,0);
  400.     Append(&Strokes,(long)theStroke);
  401.     RecognizeStroke(0);
  402.     while (IN_PROXIMITY
  403.                 && TickCount() - theStroke->end_time < MULTI_TIME_OUT
  404.                 && StrokeMayContinue())
  405.         {
  406.         EventRecord event;
  407.         if (GetOSEvent(mDownMask,&event)) {
  408.             Point where;
  409.             if (Wacom) {
  410.                 where.h = TRecord->xCoord;
  411.                 where.v = TRecord->yCoord;
  412.                 }
  413.             else {
  414.                 where = event.where;
  415.                 GlobalToLocal(&where);
  416.                 }
  417.             
  418.              theStroke = calloc(1,sizeof(StrokeData));
  419.             if (theStroke == NIL) {SysBeep(1); Debugger(); ExitToShell();}
  420.              theStroke->start_time = event.when;
  421.             CollectStroke(where,theStroke);
  422.             FlushEvents(everyEvent,0);
  423.             Append(&Strokes,(long)theStroke);
  424.             RecognizeStroke(Strokes->num_items - 1);
  425.             }
  426.         }
  427.  
  428.     code = Recognize();
  429.     ComputeBBox();
  430.      if (Strokes)
  431.          for (i = 0; i < Strokes->num_items; i++) {
  432.              StrokePtr theStroke = Strokes->items[i];
  433.              PenState thePnState;
  434.              GetPenState(&thePnState);
  435.              PenMode(patXor);
  436.              for (j = 0; j < theStroke->Ink_Num; j++) {
  437.                  Point drawme,p = theStroke->Ink[j];
  438.                 if (Wacom) {
  439.                     drawme.h = TAB_TO_SCREEN_X(p.h);
  440.                     drawme.v = TAB_TO_SCREEN_Y(p.v);
  441.                     GlobalToLocal(&drawme);
  442.                     }
  443.                 else
  444.                     drawme = p;
  445.                  MoveTo(drawme.h,drawme.v);
  446.                  LineTo(drawme.h,drawme.v);
  447.                  }
  448.              SetPenState(&thePnState);
  449.              }
  450.     FreeStrokes();
  451.     return code;
  452.     }
  453.  
  454. short        VoteWeight[]            = {3,3,3,3,3,3,3,3,3};
  455. char        Bits[]                    = {2,2,2,2,3,2,2,2,2};
  456. VoidFunc    HashFunctions[]        = {Fxn1,Fxn2,Fxna,Fxnb,Fxnc,Fxnd,Fxn4,Fxn5,FxnU};
  457. char        *Names[NUM_FEATURES]    = {"WNES","ABCD"};
  458.